#include "ov2640.h"
#include "ov2640cfg.h"
#include <stdio.h>
	  
/*********************I2C driver functions********************/
//起始条件
void SCCB_init(void)
{
    I2C->CONTROL = I2C_SDA_Msk | I2C_SCL_Msk;
    delay_us(50);
    I2C->CONTROLC = I2C_SDA_Msk;
    delay_us(50);
}
//终止条件
void SCCB_stop(void)
{
    I2C->CONTROLC = I2C_SDA_Msk;
    delay_us(50);
    I2C->CONTROL = I2C_SCL_Msk;
    delay_us(50);
    I2C->CONTROL = I2C_SDA_Msk;
}
			 	
 // Write 8 bits of data to the serial bus
 void I2c_send_byte(unsigned char c)
{
    int loop;

    for (loop = 0; loop < 8; loop++) {
        I2C->CONTROLC = I2C_SCL_Msk;
        delay_us(10);
        if (c & (1 << (7 - loop)))
            I2C->CONTROL = I2C_SDA_Msk;
        else
            I2C->CONTROLC = I2C_SDA_Msk;
        delay_us(10);
        I2C->CONTROL = I2C_SCL_Msk;
        delay_us(10);
        I2C->CONTROLC = I2C_SCL_Msk;
        delay_us(10);
    }

    I2C->CONTROL = I2C_SDA_Msk;
    delay_us(50);
 }
 // Read 8 bits of data from the serial bus
 unsigned char I2c_receive_byte(void)
 {
    int data, loop;
    data = 0;

    I2C->CONTROL = I2C_SDA_Msk;
    delay_us(10);

    for (loop = 0; loop < 8; loop++) {
        I2C->CONTROLC = I2C_SCL_Msk;
        delay_us(10);
        I2C->CONTROL = I2C_SCL_Msk | I2C_SDA_Msk;
        delay_us(10);
        if ((I2C->CONTROL & I2C_SDA_Msk))
            data += (1 << (7 - loop));
        delay_us(10);
        I2C->CONTROLC = I2C_SCL_Msk;
        delay_us(10);
    }

    I2C->CONTROLC = I2C_SDA_Msk;
    delay_us(50);

    return data;
 }

 // Read the acknowledge bit
 int I2c_receive_ack(void)
 {
    int nack;

    I2C->CONTROL = I2C_SDA_Msk;
    delay_us(10);
    I2C->CONTROLC = I2C_SCL_Msk;
    delay_us(10);
    I2C->CONTROL = I2C_SCL_Msk;
    delay_us(10);
    nack = I2C->CONTROL & I2C_SDA_Msk;
    delay_us(10);
    I2C->CONTROLC = I2C_SCL_Msk;
    delay_us(10);
    I2C->CONTROL = I2C_SDA_Msk;
    delay_us(50);
    if(nack==0)
        return 1;

    return 0;
 }

 // Write the acknowledge bit
 void I2c_send_ack(void)
 {
    I2C->CONTROLC = I2C_SCL_Msk;
    delay_us(10);
    I2C->CONTROL = I2C_SDA_Msk;
    delay_us(10);
    I2C->CONTROL = I2C_SCL_Msk;
    delay_us(10);
    I2C->CONTROLC = I2C_SCL_Msk;
    delay_us(10);
    I2C->CONTROLC = I2C_SDA_Msk;
    delay_us(50);
 }

 // Write data stream and read one byte
 unsigned char I2c_read(unsigned char reg_addr)
 {
    unsigned char rxdata;

    // Start bit
    SCCB_init();
    // Set serial and register address
    I2c_send_byte(SCCB_ID);
	delay_us(100);
    I2c_send_byte(reg_addr);
	delay_us(100);
    SCCB_stop();
    delay_us(100);
    // Start bit
    SCCB_init();
    // Read from serial address
    I2c_send_byte(SCCB_ID | 1);
	delay_us(100);
    rxdata = I2c_receive_byte();
    I2c_send_ack();
    // Actual stop bit
    SCCB_stop();

    return rxdata;
 }

 void I2c_write(unsigned char reg_addr, unsigned char data_byte)
 {
    // Start bit
    SCCB_init();
    // Set serial and register address
    I2c_send_byte(SCCB_ID);
	delay_us(100);
    I2c_send_byte(reg_addr);
	delay_us(100);
    I2c_send_byte(data_byte);
    SCCB_stop();
 }

//Initialize
unsigned char OV2640_Init(void)
{
    unsigned int i=0;
    unsigned int reg=0;
    // OV2640_PWDN=0
	GPIO0->DATAOUT&= 0xFD;
	delay_ms(10);
    // OV2640_RST=0
	GPIO0->DATAOUT &= 0xFE;
	delay_ms(10);
    // OV2640_RST=1
	GPIO0->DATAOUT |= 0x01;
    I2c_write(OV2640_DSP_RA_DLMT, 0x01);
    I2c_write(OV2640_SENSOR_COM7, 0x80);
	delay_ms(50);
    reg=I2c_read(OV2640_SENSOR_MIDH);	//读取厂家ID 高八位
	reg<<=8;
	reg|=I2c_read(OV2640_SENSOR_MIDL);	//读取厂家ID 低八位
	if(reg!=OV2640_MID)
	{
		printf("Wrong MID:%d\r\n",reg);
		return 1;
	}
	reg=I2c_read(OV2640_SENSOR_PIDH);	//读取厂家ID 高八位
	reg<<=8;
	reg|=I2c_read(OV2640_SENSOR_PIDL);	//读取厂家ID 低八位
    if(reg!=OV2640_PID)
	{
		printf("Wrong HID:%d\r\n",reg);
		return 2;
	}   
 	//初始化 OV2640,采用SVGA分辨率(800*600)
	for(i=0;i<sizeof(ov2640_svga_init_reg_tbl)/2;i++)
	{
	   	I2c_write(ov2640_svga_init_reg_tbl[i][0],ov2640_svga_init_reg_tbl[i][1]);
 	} 
  	return 0x00; 	//ok

}

//OV2640切换为RGB565模式
void OV2640_RGB565_Mode(void) 
{
	unsigned int i=0;
	//设置:RGB565输出
	for(i=0;i<(sizeof(ov2640_rgb565_reg_tbl)/2);i++)
	{
		I2c_write(ov2640_rgb565_reg_tbl[i][0],ov2640_rgb565_reg_tbl[i][1]); 
	} 
}

//自动曝光设置参数表,支持5个等级
const static unsigned char OV2640_AUTOEXPOSURE_LEVEL[5][8]=
{
	{
		0xFF,0x01,
		0x24,0x20,
		0x25,0x18,
		0x26,0x60,
	},
	{
		0xFF,0x01,
		0x24,0x34,
		0x25,0x1c,
		0x26,0x00,
	},
	{
		0xFF,0x01,	
		0x24,0x3e,	
		0x25,0x38,
		0x26,0x81,
	},
	{
		0xFF,0x01,
		0x24,0x48,
		0x25,0x40,
		0x26,0x81,
	},
	{
		0xFF,0x01,	
		0x24,0x58,	
		0x25,0x50,	
		0x26,0x92,	
	},
};

//OV2640自动曝光等级设置
//level:0~4
void OV2640_Auto_Exposure(unsigned char level)
{  
	unsigned char i;
	unsigned char *p=(unsigned char*)OV2640_AUTOEXPOSURE_LEVEL[level];
	for(i=0;i<4;i++)
	{ 
		I2c_write(p[i*2],p[i*2+1]); 
	} 
}

//白平衡设置
//0:自动
//1:太阳sunny
//2,阴天cloudy
//3,办公室office
//4,家里home
void OV2640_Light_Mode(unsigned char mode)
{
	unsigned char regccval=0X5E;//Sunny 
	unsigned char regcdval=0X41;
	unsigned char regceval=0X54;
	switch(mode)
	{ 
		case 0://auto 
			I2c_write(0XFF,0X00);	 
			I2c_write(0XC7,0X00);//AWB ON 
			return;  	
		case 2://cloudy
			regccval=0X65;
			regcdval=0X41;
			regceval=0X4F;
			break;	
		case 3://office
			regccval=0X52;
			regcdval=0X41;
			regceval=0X66;
			break;	
		case 4://home
			regccval=0X42;
			regcdval=0X3F;
			regceval=0X71;
			break;	
	}
	I2c_write(0XFF,0X00);	 
	I2c_write(0XC7,0X40);	//AWB OFF 
	I2c_write(0XCC,regccval); 
	I2c_write(0XCD,regcdval); 
	I2c_write(0XCE,regceval);  
}

//亮度设置
//0:(0X00)-2
//1:(0X10)-1
//2,(0X20) 0
//3,(0X30)+1
//4,(0X40)+2
void OV2640_Brightness(unsigned char bright)
{
  I2c_write(0xff, 0x00);
  I2c_write(0x7c, 0x00);
  I2c_write(0x7d, 0x04);
  I2c_write(0x7c, 0x09);
  I2c_write(0x7d, bright<<4); 
  I2c_write(0x7d, 0x00); 
}

//对比度设置
//0:-2
//1:-1
//2,0
//3,+1
//4,+2
void OV2640_Contrast(unsigned char contrast)
{
	unsigned char reg7d0val=0X20;//默认为普通模式
	unsigned char reg7d1val=0X20;
  	switch(contrast)
	{
		case 0://-2
			reg7d0val=0X18;	 	 
			reg7d1val=0X34;	 	 
			break;	
		case 1://-1
			reg7d0val=0X1C;	 	 
			reg7d1val=0X2A;	 	 
			break;	
		case 3://1
			reg7d0val=0X24;	 	 
			reg7d1val=0X16;	 	 
			break;	
		case 4://2
			reg7d0val=0X28;	 	 
			reg7d1val=0X0C;	 	 
			break;	
	}
	I2c_write(0xff,0x00);
	I2c_write(0x7c,0x00);
	I2c_write(0x7d,0x04);
	I2c_write(0x7c,0x07);
	I2c_write(0x7d,0x20);
	I2c_write(0x7d,reg7d0val);
	I2c_write(0x7d,reg7d1val);
	I2c_write(0x7d,0x06);
}

//设置传感器输出窗口 
//sx,sy,起始地址
//width,height:宽度(对应:horizontal)和高度(对应:vertical)
void OV2640_Window_Set(unsigned int sx,unsigned int sy,unsigned int width,unsigned int height)
{
	unsigned int endx;
	unsigned int endy;
	unsigned char temp; 
	endx=sx+width/2;	//V*2
 	endy=sy+height/2;
	
	I2c_write(0XFF,0X01);			
	temp=I2c_read(0X03);				//读取Vref之前的值
	temp&=0XF0;
	temp|=((endy&0X03)<<2)|(sy&0X03);
	I2c_write(0X03,temp);				//设置Vref的start和end的最低2位
	I2c_write(0X19,sy>>2);			//设置Vref的start高8位
	I2c_write(0X1A,endy>>2);			//设置Vref的end的高8位
	
	temp=I2c_read(0X32);				//读取Href之前的值
	temp&=0XC0;
	temp|=((endx&0X07)<<3)|(sx&0X07);
	I2c_write(0X32,temp);				//设置Href的start和end的最低3位
	I2c_write(0X17,sx>>3);			//设置Href的start高8位
	I2c_write(0X18,endx>>3);			//设置Href的end的高8位
}

//设置图像输出大小
//OV2640输出图像的大小(分辨率),完全由该函数确定
//width,height:宽度(对应:horizontal)和高度(对应:vertical),width和height必须是4的倍数
//返回值:0,设置成功
//    其他,设置失败
unsigned char OV2640_OutSize_Set(unsigned int width,unsigned int height)
{
	unsigned int outh;
	unsigned int outw;
	unsigned char temp; 
	if(width%4)return 1;
	if(height%4)return 2;
	outw=width/4;
	outh=height/4; 
	I2c_write(0XFF,0X00);	
	I2c_write(0XE0,0X04);			
	I2c_write(0X5A,outw&0XFF);		//设置OUTW的低八位
	I2c_write(0X5B,outh&0XFF);		//设置OUTH的低八位
	temp=(outw>>8)&0X03;
	temp|=(outh>>6)&0X04;
	I2c_write(0X5C,temp);				//设置OUTH/OUTW的高位 
	I2c_write(0XE0,0X00);	
	return 0;
}

//设置图像开窗大小
//由:OV2640_ImageSize_Set确定传感器输出分辨率从大小.
//该函数则在这个范围上面进行开窗,用于OV2640_OutSize_Set的输出
//注意:本函数的宽度和高度,必须大于等于OV2640_OutSize_Set函数的宽度和高度
//     OV2640_OutSize_Set设置的宽度和高度,根据本函数设置的宽度和高度,由DSP
//     自动计算缩放比例,输出给外部设备.
//width,height:宽度(对应:horizontal)和高度(对应:vertical),width和height必须是4的倍数
//返回值:0,设置成功
//    其他,设置失败
unsigned char OV2640_ImageWin_Set(unsigned int offx,unsigned int offy,unsigned int width,unsigned int height)
{
	unsigned int hsize;
	unsigned int vsize;
	unsigned char temp; 
	if(width%4)return 1;
	if(height%4)return 2;
	hsize=width/4;
	vsize=height/4;
	I2c_write(0XFF,0X00);	
	I2c_write(0XE0,0X04);					
	I2c_write(0X51,hsize&0XFF);		//设置H_SIZE的低八位
	I2c_write(0X52,vsize&0XFF);		//设置V_SIZE的低八位
	I2c_write(0X53,offx&0XFF);		//设置offx的低八位
	I2c_write(0X54,offy&0XFF);		//设置offy的低八位
	temp=(vsize>>1)&0X80;
	temp|=(offy>>4)&0X70;
	temp|=(hsize>>5)&0X08;
	temp|=(offx>>8)&0X07; 
	I2c_write(0X55,temp);				//设置H_SIZE/V_SIZE/OFFX,OFFY的高位
	I2c_write(0X57,(hsize>>2)&0X80);	//设置H_SIZE/V_SIZE/OFFX,OFFY的高位
	I2c_write(0XE0,0X00);	
	return 0;
}

//该函数设置图像尺寸大小,也就是所选格式的输出分辨率
//UXGA:1600*1200,SVGA:800*600,CIF:352*288
//width,height:图像宽度和图像高度
//返回值:0,设置成功
//    其他,设置失败
unsigned char OV2640_ImageSize_Set(unsigned int width,unsigned int height)
{ 
	unsigned char temp; 
	I2c_write(0XFF,0X00);			
	I2c_write(0XE0,0X04);			
	I2c_write(0XC0,(width)>>3&0XFF);		//设置HSIZE的10:3位
	I2c_write(0XC1,(height)>>3&0XFF);		//设置VSIZE的10:3位
	temp=(width&0X07)<<3;
	temp|=height&0X07;
	temp|=(width>>4)&0X80; 
	I2c_write(0X8C,temp);	
	I2c_write(0XE0,0X00);				 
	return 0;
}
